package gnetty

import (
	"gitee.com/llakcs/gnetty/commom"
	"gitee.com/llakcs/gnetty/config"
	"github.com/panjf2000/ants/v2"
	"log"
	"net"
	"runtime"
	"time"
)

type TcpServer struct {
	Config  []*config.ServerConfig //socket通道切片
	Address string                 //监听IP地址
	MPool   *ants.Pool             //主线程池
	WPool   *ants.Pool             //工作线程池
}

//创建tcp服务端
func NewTcpServer(mainSize, workerSize int, options ...*config.ServerConfig) *TcpServer {
	if mainSize == 0 {
		mainSize = runtime.NumCPU() * 3
	}
	if workerSize == 0 {
		workerSize = runtime.NumCPU() * 6
	}
	mainPool, _ := ants.NewPool(mainSize, ants.WithPreAlloc(true))
	workerPool, _ := ants.NewPool(workerSize, ants.WithPreAlloc(true))
	s := &TcpServer{
		Config: make([]*config.ServerConfig, 0),
		MPool:  mainPool,
		WPool:  workerPool,
	}
	for _, o := range options {
		if o.DecoderHandler == nil {
			panic("###自定义解码器不能为空")
		}
		if o.ChanReadHandler == nil {
			panic("###自定义业务处理器不能为空")
		}
		if o.EncodeHandler == nil {
			panic("###自定义编码器不能为空")
		}
		s.Config = append(s.Config, o)
	}
	return s
}

/**
 *  动态调整io线程池 0不需要调整
 */
func (s *TcpServer) TunePoolSize(workerSize int) {
	if workerSize > 0 {
		s.WPool.Tune(workerSize)
	}
}

/**
 * 动态调整主线程池
 */
func (s *TcpServer) TuneMainPoolSize(mainSize int) {
	if mainSize > 0 {
		s.MPool.Tune(mainSize)
	}
}

//设置TCP监听地址
func (s *TcpServer) ListenTcp() {
	//释放线程池
	defer s.WPool.Release()
	defer s.MPool.Release()
	for _, o := range s.Config {
		if !o.Enable {
			continue
		}
		if o.ChanSize == 0 {
			o.ChanSize = 1024
		}
		//io线程通道
		iochan := make(chan *commom.ChannelContext, o.ChanSize)
		//处理系统配置
		handlerConfig(o, s, iochan)
		//处理io操作
		handlerIo(s, iochan)
	}
	select {}
}

func handlerConfig(o *config.ServerConfig, s *TcpServer, iochan chan *commom.ChannelContext) {
	go func(o *config.ServerConfig) {
		log.Println("###添加监听端口:" + o.Port)
		addr, _ := net.ResolveTCPAddr("tcp4", s.Address+":"+o.Port)
		listener, _ := net.ListenTCP("tcp4", addr)
		for {
			conn, err := listener.AcceptTCP()
			if err != nil {
				log.Println("###异常", err.Error())
				//删除异常的连接
				//delete(ctx.ContextMap, conn.RemoteAddr().String())
			}
			log.Println("###conn.remote addr:", conn.RemoteAddr(), "local addr:", conn.LocalAddr())
			//使用主线程处理连接
			s.MPool.Submit(func() {
				//创建channelContext
				ctx := &commom.ChannelContext{
					Conn:     conn,
					DecodeH:  o.DecoderHandler,
					EncodeH:  o.EncodeHandler,
					ChannelH: o.ChanReadHandler,
					Buf:      nil,
				}
				handleConn(ctx, o, iochan)
			})
		}
	}(o)
}

//处理io操作
func handlerIo(s *TcpServer, iochan chan *commom.ChannelContext) {
	go func() {
		for {
			select {
			case ctx := <-iochan:
				s.WPool.Submit(func() {
					ctx.DecodeH(ctx)
				})
			}
		}
	}()
}

//处理客户端连接
func handleConn(ctx *commom.ChannelContext, o *config.ServerConfig, iochan chan *commom.ChannelContext) {
	buf := make([]byte, o.BufferSize) //设置读取数据缓冲区
	defer ctx.Conn.Close()            // close connection before exit
	if o.ReadTimeout != 0 {
		ctx.Conn.SetReadDeadline(time.Now().Add(o.ReadTimeout)) //设置连接超时时间
	}
	for {
		read_len, err := ctx.Conn.Read(buf) //读取客户端发来的数据
		if read_len == 0 {
			err = &commom.ReadLenError{}
		}
		if err != nil {
			log.Println("##连接发生异常，remote addr:", ctx.Conn.RemoteAddr(), "异常原因:", err)
			//stores := engine.GetEngine().StoreMap
			//for _, v := range stores {
			//	if v.Conn.RemoteAddr() == conn.RemoteAddr() {
			//		logcat.GetLog().Sugar().Info("###id:", v.StoreID, "已离线")
			//		//设置离线
			//		v.SetAlive(false)
			//	}
			//}
			return
		}
		ctx.Buf = buf[:read_len]
		//发送到io线程池
		iochan <- ctx
	}
}
